<?php

namespace App\Command\ResumeParsing\Service\ParsingSpecial;

use App\Command\ResumeParsing\Service\ParsingHelper;

class ParsingSpecial {
    protected $func;
    protected $tagNotExist;
    protected $helper;
    public $get = array();
    protected $getBackup = array();
    protected $groupGetter = array();
    protected $groupCache = array();
    public $debug = false;

    //在派生类构造函数中对get属性载入自定义读取方法
    public function __construct(ParsingHelper $helper) {
        $this->helper = $helper;
        $this->func = $helper->func;
        $this->tagNotExist = function () {
            return $this->debug ? 'TagNotExist' : '';
        };
    }

    /**
     * 固定格式简历初始化时必须调用此方法一次
     * @param mixed ...$params 简历数据源对象
     * @return bool 是否符合当前渠道的简历特征
     */
    public function parse(...$params) {
        if (!$this->canParse(...$params)) {
            return false;
        }
        $this->onParsed();
        return true;
    }

    /**
     * 检测简历是否适用当前规则集（不进行完整初始化，仅用于判断简历类型）
     * @param mixed ...$params 简历数据源对象
     * @return bool 是否符合当前渠道的简历特征
     */
    public function canParse(...$params) {
        $this->onParseInit(...$params);
        $this->groupCache = array('comment' => '');
        if (!empty($this->getBackup)) {
            $tmp =& $this->get;
            $this->get =& $this->getBackup;   //重新载入正确的get方法
            $this->getBackup =& $tmp;
        }
        if (!$this->test()) {   //无法正常解析
            $tmp =& $this->get;
            $this->get =& $this->getBackup;   //提供一套无功能的get方法
            $this->getBackup =& $tmp;
            return false;
        }
        return true;
    }

    //自定义初始化数据逻辑，可以被重载
    protected function onParseInit(...$params) {
    }

    //自定义parse后行为，可以被重载
    protected function onParsed() {
    }

    //检测简历是否适用当前规则集，可以被重载自定义检测方式
    protected function test() {
        if (empty($this->get)) return false;
        if (empty($this->get['name']) || !is_callable($this->get['name']) || empty($this->get['name']())) return false;
        if (empty($this->get['mobile']) || !is_callable($this->get['mobile']) || empty($this->get['mobile']())) return false;
        return true;
    }

    /**
     * 调用groupParse时，对未归类数据的默认处理方式
     * @param mixed $item 数据来源对象
     * @return string 可记录文本
     */
    protected function groupFormatCallback($item) {
        return $item;
    }

    //根据一个Node集合一次性获取多个字段
    protected function groupParse($groupName, $dataSet, $return = false, callable $callback = null) {
        if (!$this->is_iterable($dataSet)) return;
        if (!is_callable($callback)) {
            $callback = [$this, 'groupFormatCallback'];
        }
        if ($return) {
            $result = array('comment' => '');
        } else {
            $this->groupCache[$groupName] = true;
            $result = &$this->groupCache;
        }
        $cur = 1;
        if (isset($dataSet[0]) && is_array($this->groupGetter[$groupName])) {
            foreach ($dataSet as $item) {
                for ($i = $cur; $i < count($this->groupGetter[$groupName]); $i++) {
                    if ($this->cloneDict($result, $this->groupGetter[$groupName][$i]($item))) {
                        $cur++;
                        continue 2;
                    }
                }
                if (is_callable($callback)) $item = $callback($item);
                $result['comment'] .= "\r\n" . $item;
            }
        } else {
            $this->cloneDict($result, $dataSet);
        }
        $result['comment'] = trim($result['comment']);
        if ($return) return $result;
    }

    //浅表复制source的键值对追加到target字典中
    protected function cloneDict(&$target, $source) {
        if (!$this->is_iterable($source)) return false;
        $success = false;
        foreach ($source as $k => $v) {
            if (!empty($target[$k]) && is_string($target[$k])) {
                $target[$k] .= "\r\n" . $v;
            } else {
                $target[$k] = $v;
            }
            $success = true;
        }
        return $success;
    }

    protected function is_iterable($target) {
        return is_array($target) || (is_object($target) && ($target instanceof \Traversable));
    }

    /**
     * 获取组当中的字段，具备缓存机制，通常写作$this->get[<name>] = $this->groupGet(<name>, <groupName>);
     * @param string $name 字段名
     * @param string $groupName 组名
     * @return \Closure 一个包含被封装的解析逻辑的方法
     */
    protected function groupGet(string $name, string $groupName) {
        return function () use ($name, $groupName) {
            if (!isset($this->groupCache[$name]) && !isset($this->groupCache[$groupName])) {
                $this->groupProcess($groupName, null, false);
            }
            return $this->groupCache[$name] ?? '';
        };
    }

    protected function groupProcess($groupName, $params = null, $return = true) {
        if (isset($this->groupGetter[$groupName])) {
            $group = &$this->groupGetter[$groupName];
            if (is_array($group) && isset($group[0])) {
                $method = &$group[0];
            } else {
                $method = &$group;
            }
        }
        if (isset($method) && is_callable($method)) {
            if (is_null($params)) {
                return $this->groupParse($groupName, $method(), $return);
            } else {
                return $this->groupParse($groupName, $method($params), $return);
            }
        }
    }

    protected function groupByBoundary($dataSet, callable $testBoundary) {
        if (!$this->is_iterable($dataSet)) throw new \InvalidArgumentException('$dataSet should be array or traversable');
        $result = [];
        foreach ($dataSet as $item) {
            if ($testBoundary($item)) {
                if (isset($group)) $result[] = $group;
                $group = [$item];
            } elseif (isset($group)) {
                $group[] = $item;
            }
        }
        if (isset($group)) $result[] = $group;
        return $result;
    }

    protected function formatTxt($text, ...$params) {
        return $this->func->formatTxt($text, ...$params);
    }

    protected function preg_get($pattern, $subject, $options = []) {
        return $this->func->preg_get($pattern, $subject, $options);
    }
}